/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_zml.htm
 *
 * Zhiqim Zml is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.zml.loader;

import java.net.URL;
import java.net.URLConnection;

import org.zhiqim.kernel.util.Resources;
import org.zhiqim.kernel.util.Streams;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.zml.Zml;
import org.zhiqim.zml.ZmlCache;
import org.zhiqim.zml.ZmlEngine;
import org.zhiqim.zml.ZmlLoader;

/**
 * 类路径ZML加载器，指定类根路径和加载类，传入相对路径即可
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class ClassZmlLoader implements ZmlLoader
{
    private final ZmlEngine engine;             //ZML引擎
    private final ZmlCache cache;               //ZML缓存
    private final ClassZmlWatcher watcher;      //类ZML监视
    
    private final Class<?> loadClass;           //加载类
    private final String contextPath;           //根路径
    
    /**
     * 类路径ZML加载器构造方法
     * 
     * @param engine        ZML引擎
     * @param loadClass     指定可访问到ZML文件的加载类
     * @param contextPath    ZML路径前缀
     */
    public ClassZmlLoader(ZmlEngine engine, Class<?> loadClass, String contextPath)
    {
        //对pathPrefix检查和处理，要求格式为/org/zhiqim/example/这种格式，最终会增加前缀/和删除后面的/
        contextPath = Strings.addStartsWith(contextPath, "/");
        contextPath = Strings.removeEndsWith(contextPath, "/");
        
        this.engine = engine;
        this.loadClass = loadClass;
        this.contextPath = contextPath;
        this.cache = new ZmlCache(engine.getMaxIdleTime(), engine.getMaxKeepTime());
        this.watcher = new ClassZmlWatcher(loadClass, contextPath);
    }
    
    /** 获取上下文环境路径 */
    public String getContextPath()
    {
        return contextPath;
    }
    
    @Override
    public boolean hasZml(String path) throws Exception
    {
        return getLastModified(path) != -1;
    }
    
    @Override
    public Zml loadZml(String path) throws Exception
    {
        long lastModified = getLastModified(path);
        if (lastModified == -1)
        {
            cache.remove(path);
            return null;
        }
        
        Zml zml = cache.get(path);
        if (zml != null && lastModified == zml.getLastModified())
        {//在缓存中找到，并且缓存最修改时间和文件相同返回缓存中的ZML
            return zml;
        }
        
        //未找到或最后修改时间不同，则重找
        URL url = Resources.getResource(loadClass, contextPath + path);
        URLConnection conn = url.openConnection();
        String content = Streams.getString(conn.getInputStream(), engine.getEncoding());
        zml = new Zml(engine, path, lastModified, content);
        cache.put(path, zml);
        return zml;
    }

    @Override
    public long getLastModified(String path) throws Exception
    {
        if (!Strings.startsWith(path, "/") || !Validates.isMatch(path, engine.getPatterns()))
        {//要求相对于根目录的绝对路径
            throw new Exception("ZML["+path+"]配置不正确，必须/开头且符合配置的模式");
        }
        
        return watcher.getLastModified(path);
    }
    
    @Override
    public void setMaxIdleTime(int maxIdleTime)
    {
        this.cache.setMaxIdleTime(maxIdleTime);
    }
    
    @Override
    public void setMaxKeepTime(int maxKeepTime)
    {
        this.cache.setMaxKeepTime(maxKeepTime);
    }
}
